/****h* scdbxml/xpath.c
 *  NAME
 *    xpath.c
 *  DESCRIPTION
 *********/
#include "xpath.h"

/****f* xpath.c/dumpXPathOp
 *  NAME
 *    dumpXPathOp
 *  SYNOPSIS
 *    void dumpXPathOp(FILE *output, 
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
void dumpXPathOp(FILE *output, 
								 xmlXPathCompExprPtr comp, 
								 xmlXPathStepOpPtr op,
								 int haveCollected) 
{
	 if (op == NULL) {
			fprintf(output, "Step is NULL\n");
			return;
	 }
	 
	 switch (op->op) {
		case XPATH_OP_END:
	    /*fprintf(output, "END");*/
			break;

			/*
			 * boolean operators
			 */
		case XPATH_OP_AND:
	    fprintf(output, "AND");
			break;
		case XPATH_OP_OR:
	    fprintf(output, "OR");
			break;

			/*
			 * arithmetic operators
			 */
		case XPATH_OP_EQUAL:
			if (op->value)
				fprintf(output, "EQUAL =");
			else
				fprintf(output, "EQUAL !=");
			break;
		case XPATH_OP_CMP:
			if (op->value)
				fprintf(output, "CMP <");
			else
				fprintf(output, "CMP >");
			if (!op->value2)
				fprintf(output, "=");
			break;
		case XPATH_OP_PLUS:
			if (op->value == 0)
				fprintf(output, "PLUS -");
			else if (op->value == 1)
				fprintf(output, "PLUS +");
			else if (op->value == 2)
				fprintf(output, "PLUS unary -");
			else if (op->value == 3)
				fprintf(output, "PLUS unary - -");
			break;
		case XPATH_OP_MULT:
			if (op->value == 0)
				fprintf(output, "MULT *");
			else if (op->value == 1)
				fprintf(output, "MULT div");
			else
				fprintf(output, "MULT mod");
			break;

			/*
			 * joining two XPath statements
			 */
		case XPATH_OP_UNION:
			fprintf(output, "UNION");
			break;
			/*
			 * match root node
			 */
		case XPATH_OP_ROOT:
			fprintf(output, "ROOT");
			break;
			/*
			 * match context node
			 */
		case XPATH_OP_NODE:
			fprintf(output, "NODE");
			break;
		case XPATH_OP_RESET:
			/*fprintf(output, "RESET");*/
			break;

			/*
			 * sort nodes
			 */
		case XPATH_OP_SORT:
			fprintf(output, "SORT");
			break;
			
			/*
			 * looking for a node
			 */
		case XPATH_OP_COLLECT: {
			 xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value;
			 xmlXPathTestVal test = (xmlXPathTestVal) op->value2;
			 xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3;

			 const xmlChar *prefix = op->value4;
			 const xmlChar *name = op->value5;
			 
			 /*
				* only want first selection of nodes -
				* following selections are just to test for existence
				*/
			 if (haveCollected) 
				 {
						fprintf(output, "EXISTS ");
				 }
			 else 
				 {
						fprintf(output, "COLLECT ");
						haveCollected = 1;
				 }

			 /*
				* axis - relation to next node in list
				*/
			 switch (axis) {
				case AXIS_ANCESTOR:
					fprintf(output, " 'ancestors' ");
					break;
				case AXIS_ANCESTOR_OR_SELF:
					fprintf(output, " 'ancestors-or-self' ");
					break;
				case AXIS_ATTRIBUTE:
					fprintf(output, " 'attributes' ");
					break;
				case AXIS_CHILD:
					fprintf(output, " 'child' ");
					break;
				case AXIS_DESCENDANT:
					fprintf(output, " 'descendant' ");
					break;
				case AXIS_DESCENDANT_OR_SELF:
					fprintf(output, " 'descendant-or-self' ");
					break;
				case AXIS_FOLLOWING:
					fprintf(output, " 'following' ");
					break;
				case AXIS_FOLLOWING_SIBLING:
					fprintf(output, " 'following-siblings' ");
					break;
				case AXIS_NAMESPACE:
					fprintf(output, " 'namespace' ");
					break;
				case AXIS_PARENT:
					fprintf(output, " 'parent' ");
					break;
				case AXIS_PRECEDING:
					fprintf(output, " 'preceding' ");
					break;
				case AXIS_PRECEDING_SIBLING:
					fprintf(output, " 'preceding-sibling' ");
					break;
				case AXIS_SELF:
					fprintf(output, " 'self' ");
					break;
			 }
			 
			 /*
				* what type of test
				*/
			 switch (test) {
				case NODE_TEST_NONE:
					fprintf(output, "'none' ");
					break;
				case NODE_TEST_TYPE:
					fprintf(output, "'type' ");
					break;
				case NODE_TEST_PI:
					fprintf(output, "'PI' ");
					break;
				case NODE_TEST_ALL:
					fprintf(output, "'all' ");
					break;
				case NODE_TEST_NS:
					fprintf(output, "'namespace' ");
					break;
				case NODE_TEST_NAME:
					fprintf(output, "'name' ");
					break;
			 }
			 
			 /*
				* type of node
				*/
			 switch (type) {
					/* element or attribute */
				case NODE_TYPE_NODE:
					fprintf(output, "'node' ");
					break;
				case NODE_TYPE_COMMENT:
					fprintf(output, "'comment' ");
					break;
				case NODE_TYPE_TEXT:
					fprintf(output, "'text' ");
					break;
				case NODE_TYPE_PI:
					fprintf(output, "'PI' ");
					break;
			 }
			 if (prefix != NULL)
				 fprintf(output, "%s:", (const char *) prefix);
			 if (name != NULL)
				 fprintf(output, "%s", (const char *) name);
			 break;
			 
		}
			
		case XPATH_OP_VALUE: {
			 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
			 
			 fprintf(output, "ELEM ");
			 xmlXPathDebugDumpObject(output, object, 0);
			 goto finish;
		}
		case XPATH_OP_VARIABLE: {
			 const xmlChar *prefix = op->value5;
			 const xmlChar *name = op->value4;
			 
			 if (prefix != NULL)
				 fprintf(output, "VARIABLE %s:%s", 
								 (const char *) prefix, 
								 (const char *) name);
			 else
				 fprintf(output, "VARIABLE %s", (const char *) name);
			 break;
		}
		case XPATH_OP_FUNCTION: {
			 int nbargs = op->value;
			 const xmlChar *prefix = op->value5;
			 const xmlChar *name = op->value4;
			 
			 if (prefix != NULL)
				 fprintf(output, "FUNCTION %s:%s(%d args)",
								 (const char *) prefix, 
								 (const char *) name, 
								 nbargs);
			 else
				 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
			 break;
		}
		case XPATH_OP_ARG: fprintf(output, "ARG");
			break;
		case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE");
			break;
		case XPATH_OP_FILTER: fprintf(output, "FILTER");
			break;
#ifdef LIBXML_XPTR_ENABLED
		case XPATH_OP_RANGETO: fprintf(output, "RANGETO");
			break;
#endif
		default:
			fprintf(output, "UNKNOWN %d\n", op->op);
			return;
	 }
	 fprintf(output, "\n");
	 
	 finish:
	 if (op->ch1 >= 0)
		 dumpXPathOp(output, comp, &comp->steps[op->ch1], haveCollected);
    if (op->ch2 >= 0)
		 dumpXPathOp(output, comp, &comp->steps[op->ch2], haveCollected);
}

/************ dumpXPathOp */
/****f* xpath.c/dumpXPath
 *  NAME
 *    dumpXPath
 *  SYNOPSIS
 *    void dumpXPath(FILE *output, xmlXPathCompExprPtr comp)
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
void dumpXPath(FILE *output, xmlXPathCompExprPtr comp)
{
	 if ((output == NULL) || (comp == NULL)) return;

	 fprintf(output, "Compiled Expression : %d elements\n",
					 comp->nbStep);
	 dumpXPathOp(output, comp, &comp->steps[comp->last], 0);
}


/************ dumpXPath */
/****f* xpath.c/compileXPath
 *  NAME
 *    compileXPath
 *  SYNOPSIS
 *    xmlXPathCompExprPtr compileXPath(const xmlChar *str) {
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
xmlXPathCompExprPtr compileXPath(const xmlChar *str) {
	 xmlXPathParserContextPtr pctxt;
	 xmlXPathCompExprPtr comp;
	 
	 xmlXPathInit();
	 
	 pctxt = xmlXPathNewParserContext(str, NULL);
	 if (pctxt == NULL) {
			return NULL;
	 }
	 
	 xmlXPathCompileExpr(pctxt, 1);
	 
	 if( pctxt->error != XPATH_EXPRESSION_OK )
		 {
        xmlXPathFreeParserContext(pctxt);
        return(NULL);
		 }
	 
	 if (*pctxt->cur != 0) {
			comp = NULL;
	 } else {
			comp = pctxt->comp;
			pctxt->comp = NULL;
	 }
	 xmlXPathFreeParserContext(pctxt);
	 
	 if (comp != NULL) {
			comp->expr = xmlStrdup(str);
#ifdef DEBUG_EVAL_COUNTS
			comp->string = xmlStrdup(str);
			comp->nb = 0;
#endif
			if ((comp->expr != NULL) &&
					(comp->nbStep > 2) &&
					(comp->last >= 0) &&
					(xmlXPathCanRewriteDosExpression(comp->expr) == 1))
				{
					 xmlXPathRewriteDOSExpression(comp, &comp->steps[comp->last]);
				}
	 }
	 return comp;
}

/************ compileXPath */
/****f* xpath.c/main
 *  NAME
 *    main
 *  SYNOPSIS
 *    int main(int argc, char** argv) 
 *  ARGUMENTS
 *  RETURN TYPE
 *  AUTHOR
 *  SOURCE
*/
int main(int argc, char** argv) 
{
	 xmlXPathCompExprPtr comp;
	 
	 if (argc < 2) 
		 {
				fprintf(stderr, "Usage: %s xpath\n", argv[0]);
				return EXIT_FAILURE;
		 }
	 
	 comp = compileXPath((xmlChar*) argv[1]);
	 if (comp == NULL) 
		 {
				fprintf(stderr, "xpath error\n");
				return EXIT_FAILURE;
		 }
	 
	 dumpXPath(stdout, comp);
	 xmlXPathFreeCompExpr(comp);
	 
	 return EXIT_SUCCESS;
}
/************ main */
